package org.altervista.cp.microkernel;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.Date;
import java.util.Properties;

import javax.naming.NamingException;
import javax.sql.DataSource;

import org.altervista.cp.microkernel.install.InstallManager;
import org.altervista.cp.microkernel.install.InstallRegistry;
import org.altervista.cp.microkernel.install.InstallationException;
import org.altervista.cp.microkernel.install.Installer;
import org.altervista.cp.microkernel.install.KernelInstaller;
import org.altervista.cp.microkernel.io.TemporaryFileGenerator;
import org.altervista.cp.microkernel.io.WorkingDirectory;
import org.altervista.cp.microkernel.log.Log;
import org.altervista.cp.microkernel.persistence.PersistenceQuery;
import org.altervista.cp.microkernel.persistence.PersistenceSession;
import org.altervista.cp.microkernel.persistence.PersistenceSessionFactory;
import org.altervista.cp.microkernel.persistence.spi.DefaultPersistenceSessionFactory;
import org.altervista.cp.thinjdbc.ds.DefaultDataSource;
import org.picocontainer.Startable;

public class DefaultInstallManager implements InstallManager, Startable {

	private static final Log log = new Log(DefaultInstallManager.class);

	public DefaultInstallManager(WorkingDirectory wd) {
		_wd = wd;
	}

	public void checkInstaller(Installer installer)
			throws InstallationException {
		try {
			InstallRegistry ir = null;
			boolean firstInstall = false;
			PersistenceSession ps = _psf.openSession();
			try {
				PersistenceQuery<InstallRegistry> pq = ps
						.createQuery(InstallRegistry.class,
								"select status from InstallRegistry where name = ? and version = ?");
				pq.setParameter(0, installer.getName());
				pq.setParameter(1, installer.getMajorVersion() + "."
						+ installer.getMinorVersion());
				ir = pq.getUniqueResult();
			} catch (Exception e) {
				firstInstall = true;
				installer.install(_ds);
			}
			if (ir == null) {
				ir = new InstallRegistry(installer);
				ir.setStartInstallationDate(new Date());
				// ir.setInstaller(new ObjectSerializer(installer).getBlob());
				ir.setId(1);
				ps.insert(ir);
				ps.commit();
			}
			if (ir.getStatus() == null
					|| ir.getStatus().equals(
							InstallRegistry.INSTALLATION_FAILURE)) {
				try {
					if (!firstInstall) {
						installer.install(_ds);
					}
					ir.setStatus(InstallRegistry.INSTALLED);
				} catch (Throwable t) {
					log.error("Installation error", t);
					ir.setStatus(InstallRegistry.INSTALLATION_FAILURE);
				}
				ir.setEndInstallationDate(new Date());

				ps.update(ir);
			}
			ps.commit();

			ps.close();
		} catch (Throwable t) {
			throw new InstallationException(t);
		}
	}

	private DataSource _ds;
	private PersistenceSessionFactory _psf;

	public void start() {

		if (!KernelEngine.Instance.isInitialized()) {
			log.info("Starting the kernel datasource initialization");

			BootstrapContext jndiContext = null;
			try {
				jndiContext = new BootstrapContext();
			} catch (NamingException e) {
				throw new IllegalStateException(e);
			}

			String dsName = BootstrapProperties.getDataSourceName();
			boolean embedded = false;
			if (dsName == null) {
				_ds = (DataSource) jndiContext.lookup("KERNEL_DS");
				if (_ds == null) {
					try {
						_ds = new DefaultDataSource(BootstrapProperties
								.getJDBCDriver(), BootstrapProperties
								.getJDBCUrl(), BootstrapProperties
								.getJDBCUser(), BootstrapProperties
								.getJDBCPassword(), null);
						dsName = _ds.toString();
						log.info("Found datasource: {0}", dsName);
					} catch (Exception e) {
						log
								.info("No datasource has been configured: connecting to the embedded RDBMS instance");
						String rdbmsHome = _wd.getRdbmsDirectory().getPath();
						System.setProperty("derby.system.home", rdbmsHome);
						Properties userProps = new Properties();
						userProps.setProperty("derby.authentication.provider",
								"BUILTIN");
						userProps.setProperty("derby.user.SYSTEM", "manager");
						String dbUrl = rdbmsHome + "/KernelDB";
						_ds = new DefaultDataSource(
								"org.apache.derby.jdbc.EmbeddedDriver",
								"jdbc:derby:" + dbUrl + ";create=true",
								"SYSTEM", "manager", userProps);
						embedded = true;
						dsName = _ds.toString();
						log.info("Found embedded datasource: {0}", dsName);
					}
				}
			} else {
				_ds = (DataSource) jndiContext.lookup(dsName);
				if (_ds == null) {
					throw new IllegalStateException("DataSource name '"
							+ dsName + "' not found in JNDI context");
				}
				log.info("Found JNDI datasource: {0}", dsName);
			}

			try {
				Connection c = _ds.getConnection();
				if (embedded && c.getWarnings() == null) {
					log.info("First creation of the embedded RDBMS instance");
				}
				c.close();
				log
						.info(
								"Success in getting test connection from dataSource {0}",
								dsName);
			} catch (SQLException e) {
				throw new IllegalStateException(
						"Error in getting test connection from dataSource "
								+ dsName, e);
			}

			KernelEngine.Instance.setDataSource(_ds);

			log.info("The kernel datasource has been initialized");
		}

		addKernelComponents();
	}

	public void stop() {
	}

	private void addKernelComponents() {
		KernelEngine.Instance.addComponent(TemporaryFileGenerator.class);
		_psf = KernelEngine.Instance
				.addComponent(DefaultPersistenceSessionFactory.class);

		KernelEngine.Instance.addComponent(KernelInstaller.class);
	}

	private WorkingDirectory _wd;
}
